package io.traveller.apiGateway.client;

import com.alibaba.fastjson.JSON;
import io.traveller.apiGateway.client.annotation.RouteURL;
import io.traveller.apiGateway.client.constants.SecurityLevel;
import io.traveller.apiGateway.client.route.RouteExt;
import io.traveller.apiGateway.client.route.RouteURLExt;
import io.traveller.apiGateway.client.util.Utils;
import io.traveller.common.util.ReflectUtils;
import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Route客户端配置
 *
 * Created by yunai on 16/9/27.
 */
@Configuration
public class RouteClientConfiguration {

    @Bean
    public ClientInitApplicationRunner clientInitApplicationRunner() {
        return new ClientInitApplicationRunner();
    }

    /**
     * 初始化Route配置到Zookeeper
     * TODO route：目前使用的push写的方式，如果有多个人同时在开发，会互相覆盖配置。未来优化成pull-push方式。
     */
    private static class ClientInitApplicationRunner implements ApplicationRunner {

        /**
         * 扫描Controller包的初始化路径
         */
        private static final String SCAN_BASE_PACKAGE = "io.traveller";

        private final Logger logger = LoggerFactory.getLogger(ClientInitApplicationRunner.class);

        @Value("${spring.application.name}")
        private String serviceId;
        @Value("${api.gateway.route-ext.service-sys}")
        private Integer serviceSys;
        @Value("${api.gateway.route-ext.account-sys}")
        private Integer accountSys;
        @Value("${api.gateway.route-ext.auto-gen-visitor}")
        private Boolean autoGenVisitor;
        @Value("${api.gateway.route-ext.security-level-default}")
        private SecurityLevel securityLevelDefault;
        @Value("${server.context-path}")
        private String contextPath;

        @Autowired
        @SuppressWarnings("SpringJavaAutowiringInspection")
        private CuratorFramework curator;

        @Override
        public void run(ApplicationArguments applicationArguments) throws Exception {
            // 检查参数
            Assert.notNull(curator, "curator 不能为空");
            // 遍历class，寻找需要route的类
            Set<Class<?>> classes = ReflectUtils.scanClass(SCAN_BASE_PACKAGE, Controller.class);
            // 生成
            RouteExt routeExt = getRouteExt();
            for (Class<?> clazz : classes) {
                if (clazz.getAnnotation(RouteURL.class) == null) {
                    throw new RuntimeException(String.format("class(%s) 未配置RouteURL", clazz.getName()));
                }
                if (clazz.getAnnotation(RequestMapping.class) == null) {
                    throw new RuntimeException(String.format("class(%s) 未配置RequestMapping", clazz.getName()));
                }
                RequestMapping parentRequestMapping = clazz.getAnnotation(RequestMapping.class);
                RouteURL parentRouteURL = clazz.getAnnotation(RouteURL.class);
                for (Method method : clazz.getDeclaredMethods()) {
                    if (method.getAnnotation(RequestMapping.class) == null) {
                        continue;
                    }
                    if (method.getAnnotation(RouteURL.class) == null) {
                        throw new RuntimeException(String.format("class(%s) method(%s) 未配置RouteURL", clazz.getName(),
                                method.getName()));
                    }
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                    RouteURL routeURL = method.getAnnotation(RouteURL.class);
                    List<String> urls = getURLs(parentRequestMapping, requestMapping);
                    List<String> methods = getMethods(requestMapping);
                    SecurityLevel securityLevel = getSecurityLevel(routeExt, parentRouteURL, routeURL);
                    for  (String url : urls) {
                        for (String requestMethod : methods) {
                            routeExt.getUrls().add(new RouteURLExt(url, requestMethod, securityLevel,
                                    null, null));
                        }
                    }
                }
            }
            // 校验数据格式
            Utils.validRouteExt(routeExt);
            // 写入zookeeper
            curator.setData().forPath(Utils.getFullServiceId(serviceId), JSON.toJSONBytes(routeExt));
            logger.info("[run][Route初始化完成：({})]", JSON.toJSONString(routeExt));
        }

        private RouteExt getRouteExt() {
            Assert.isTrue(StringUtils.hasText(serviceId), "serverId不能为空");
            Assert.notNull(serviceSys, "serviceSys 不能为空");
//            Assert.notNull(accountSys, "accountSys 不能为空"); 例如官网，无账号体系
            Assert.notNull(autoGenVisitor, "autoGenVisitor 不能为空");
            Assert.notNull(securityLevelDefault, "securityLevelDefault 不能为空");
            Assert.isTrue(StringUtils.hasText(contextPath), "contextPath 不能为空");
            contextPath = Utils.formatURL(contextPath);
            return new RouteExt(serviceId, serviceSys, accountSys, contextPath,
                    autoGenVisitor, securityLevelDefault);
        }

        private List<String> getURLs(RequestMapping parentRequestMapping, RequestMapping requestMapping) {
            List<String> results = new ArrayList<>();
            String[] parentURLs = parentRequestMapping.value();
            String[] urls = requestMapping.value();
            for (String parentURL : parentURLs) {
                String result = parentURL;
                for (String url : urls) {
                    result = Utils.formatURL(result + "/" + url);
                    results.add(result);
                }
            }
            return results;
        }

        private List<String> getMethods(RequestMapping requestMapping) {
            List<String> results = new ArrayList<>();
            RequestMethod[] methods = requestMapping.method().length > 0 ? requestMapping.method()
                    : RequestMethod.values();
            for (RequestMethod method : methods) {
                results.add(method.toString().toUpperCase());
            }
            return results;
        }

        private SecurityLevel getSecurityLevel(RouteExt routeExt, RouteURL parentRouteURL, RouteURL routeURL) {
            if (routeURL.securityLevel().length > 1
                    || parentRouteURL.securityLevel().length > 1) {
                throw new RuntimeException("route.securityLevel 只能配置一条.");
            }
            SecurityLevel securityLevel = null;
            if (routeURL.securityLevel().length == 1) {
                securityLevel = routeURL.securityLevel()[0];
            }
            if (parentRouteURL.securityLevel().length == 1) {
                securityLevel = parentRouteURL.securityLevel()[0];
            }
            if (securityLevel == null) {
                securityLevel = routeExt.getSecurityLevelDefault();
            }
            if (securityLevel == null) {
                throw new RuntimeException("route.securityLevel 不能为空.");
            }
            return securityLevel;
        }

    }

}